package com.hm.health.Activity;

import android.app.Service;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.Environment;
import android.os.IBinder;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import com.goodix.callback.AbsOnHBDTestClientListener;
import com.goodix.comsdk.RomaDevice;
import com.goodix.comsdk.errorcode.IErrorCode;
import com.goodix.comsdk.netstack.INetStackCallback;
import com.goodix.comsdk.testsdk.IHBDTestClient;
import com.goodix.iot.libcommounication.connection.ble.BleClientConnection;
import com.goodix.libromablecom.sdk.cardiff.arguments.SetArgumentsKeyEnum;
import com.goodix.libtest.utils.ArrayUtils;
import com.hm.health.MyApplication;
import com.hm.health.R;
import com.hm.health.entity.arguments.StartArgumentsManager;
import com.hm.health.records.ecgpatch.EcgPatchRecord;
import com.hm.health.service.EcgFlashService;

import java.io.File;
import java.util.ArrayList;
import java.util.List;
import java.util.Vector;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

import static com.hm.health.service.EcgFlashService.ERROR_FAILED;

import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;

/**
 * 读取Flash界面。
 *
 * @author wangchen1
 * @version 2020-11-13
 */
public class FlashReadActivity extends AppCompatActivity {
    private static final String TAG = FlashReadActivity.class.getSimpleName();
    private static final File csvPath = new File(Environment.getExternalStorageDirectory(), "GoodixHBD/gh31x_flash_data");
    private static final File logPath = new File(csvPath, "log");

    private Button readButton;
    private TextView statusTextView;
    private ProgressBar progressBar;
    private Button eraseButton;
    private TextView note;
    private ImageView resultImg;

    private BleClientConnection bleConnection;
    private INetStackCallback bleConnectionCallbackOriginal;

    private Context mContext;
    private int mEraseTime = 0;
    private long mCalTime = 0;

    private boolean isReading = false;
    private Boolean isErasing = false;

    private static final byte[] ERASE_CMD_START = {(byte) 0xAA, 0x10, 0x04, 0x01};
    private static final byte[] ERASE_CMD_FINISH = {(byte) 0xAA, 0x10, 0x04, 0x08};
    private static final byte[] ERASE_RES_START = {(byte) 0xAA, 0x10, 0x04, 0x02};
    private static final byte[] ERASE_RES_FINISH = {(byte) 0xAA, 0x10, 0x04, 0x04};

    private static final int ERASE_TIME_OUT = 200; // 擦除时200s超时
    private static final int BASIC_PROGRESS_TIME_OUT = 5;

    private EcgFlashService.EcgFlashBinder mBinder;
    private ServiceConnection mServiceConnection;
    private boolean mLostQuery = false;
    Vector<Integer> mAllSectorIdList = new Vector<>();
    Vector<Integer> mSectorIdList = new Vector<>();
    private ScheduledExecutorService mExecutorTimeOut = null;
    private AtomicInteger mTimeOutNum = new AtomicInteger(0);
    private int mTotalNum = 0;
    private List<Long> mCostTimeList = new ArrayList<>();
    private IHBDTestClient mHbdTestClient;

    private String calAllCostTime(int remainNum, long diffTime) {
        Long single = new Long(diffTime);
        mCostTimeList.add(single);
        long totalCurTime = 0;
        long average = 0;
        int i = 0;
        if (mCostTimeList.size() > 5) {
            for (i = 1; i < 6; i++) {
                totalCurTime += mCostTimeList.get(mCostTimeList.size() - i).longValue();
            }
            average = totalCurTime / 5;
        } else {
            for (; i < mCostTimeList.size(); i++) {
                totalCurTime += mCostTimeList.get(i).longValue();
            }
            average = totalCurTime / mCostTimeList.size();
        }

        long remainTime = average * remainNum;
        long hour = remainTime / (60 * 60 * 1000);
        long minutes = (remainTime - (hour * (60 * 60 * 1000))) / (60 * 1000);
        long second = (remainTime - (hour * (1000 * 60 * 60)) - (minutes * (1000 * 60))) / 1000;
        String time;
        if (hour > 0) {
            time =  String.format("%s%s:%s%s:%s%s", hour, mContext.getString(R.string.hour), minutes,
                    mContext.getString(R.string.minutes), second, mContext.getString(R.string.second));
        } else if (minutes > 0) {
            time =  String.format("%s%s:%s%s", minutes, mContext.getString(R.string.minutes), second, mContext.getString(R.string.second));
        } else {
            time =  String.format("%s%s", second, mContext.getString(R.string.second));
        }
        return time;
    }

    EcgPatchRecord.SyncSectorCallback mSectorCallback = new EcgPatchRecord.SyncSectorCallback() {

        @Override
        public void onProgress(int totalNum, int remainNum, int finishedSectorId) {
            long diffTime = System.currentTimeMillis() - mCalTime;
            mCalTime = System.currentTimeMillis();
            Integer rmLostSectorId = null;
            Log.i(TAG, "onProgress totalNum : " + totalNum + ", remainNum : " + remainNum +
                    ", finishedSectorId : " + finishedSectorId);
            mTimeOutNum.set(0);
            for (Integer integer : mSectorIdList) {
                if (integer.intValue() == finishedSectorId) {
                    rmLostSectorId = integer;
                    break;
                }
            }
            mSectorIdList.remove(rmLostSectorId);
            for (Integer integer : mAllSectorIdList) {
                if (integer.intValue() == finishedSectorId) {
                    rmLostSectorId = integer;
                    break;
                }
            }
            mAllSectorIdList.remove(rmLostSectorId);
            runOnUiThread(() -> {
                double progress = (double) (mTotalNum - remainNum) * 100 / mTotalNum;
                progressBar.setProgress((int) progress);
                note.setText((String.format("%2.2f", progress) + "%") + "  " + mContext.getString(R.string.need) + "  " +
                        calAllCostTime(remainNum, diffTime));
            });
            if (mAllSectorIdList.isEmpty()) {

            } else {
                if (mSectorIdList.isEmpty()) {
                    int size;
                    if (mAllSectorIdList.size() > 50) {
                        size = 50;
                    } else {
                        size = mAllSectorIdList.size();
                    }
                    for (int i = 0; i < size; i++) {
                        Integer integer = new Integer(mAllSectorIdList.get(i).intValue());
                        mSectorIdList.add(integer);
                    }
                    MyApplication.getHBDTestClient().setRunTimeFlag(SetArgumentsKeyEnum.ARG_LOST_SECTOR_ID_PACKAGE, "STOP");
                    MyApplication.getHBDTestClient().setRunTimeFlag(SetArgumentsKeyEnum.ARG_LOST_SECTOR_ID_PACKAGE,
                            integerListToString(mSectorIdList));
                    Log.i(TAG,"onProgress send sector id, " + integerListToString(mSectorIdList));
                }
            }
        }

        @Override
        public void onSaveFinish() {
            if (mAllSectorIdList.isEmpty()) {
                stopGetSectorId(false);
                progressFinish();
                runOnUiThread(() -> {
                    resultImg.setImageDrawable(mContext.getDrawable(R.drawable.ic_check));
                    statusTextView.setText(R.string.ecg_finish_sync_success);
                    note.setText(R.string.finish);
                });
                Executors.newSingleThreadExecutor().execute(() -> {
                    mBinder.forceFinish();
                    mBinder.resetStartMode();
                });
            } else {
                Log.d(TAG, "not all sector finished, continue");
            }
        }
    };

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_flash_read);
        setTitle(getString(R.string.sync_ecg_data));
        mContext = this;

        statusTextView = findViewById(R.id.statusTextView);
        readButton = findViewById(R.id.readButton);
        progressBar = findViewById(R.id.progressBar);
        eraseButton = findViewById(R.id.eraseButton);
        note = findViewById(R.id.note);
        resultImg = findViewById(R.id.result);

        bleConnection = BleClientConnection.getInstance(mContext);
        bleConnectionCallbackOriginal = bleConnection.getCallback();
        mHbdTestClient = MyApplication.getHBDTestClient();
        mHbdTestClient.registerTestCallback(mClientListener, null);
        initData();

        readButton.setOnClickListener(view -> {
            runOnUiThread(() -> {
                resultImg.setImageDrawable(null);
                statusTextView.setText(R.string.status_ready);
                progressBar.setProgress(0);
                note.setText("");
            });
            mTotalNum = 0;
            mCalTime = 0;
            mCostTimeList.clear();
            mLostQuery = mBinder.isLostQuery();
            bleConnection.setCallback(bleConnectionCallbackOriginal);
            if (isReading) {
                mBinder.forceFinish();
                stopGetSectorId(false);
                progressFinish();
                mAllSectorIdList.clear();
                mSectorIdList.clear();
                runOnUiThread(() -> {
                    readButton.setEnabled(true);
                    statusTextView.setTextColor(getResources().getColor(R.color.black));
                });
            } else {
                runOnUiThread(() -> {
                    if (mLostQuery) {
                        note.setText(R.string.ecg_finish_sync_lost_data);
                    } else {
                        note.setText(R.string.ecg_finish_sync_all_data);
                    }
                });
                mTimeOutNum.set(0);
                setSectorId(false);
            }
        });

        eraseButton.setOnClickListener(view -> {
            runOnUiThread(() -> {
                note.setText(R.string.ecg_finish_erase_flash);
                progressBar.setProgress(0);
            });
            bleConnection.setCallback(new INetStackCallback() {
                @Override
                public void onStatusChanged(RomaDevice device, int status) {

                }

                @Override
                public void onDataChanged(RomaDevice device, String dataType, byte[] data) {
                    if (ArrayUtils.startsWith(data, ERASE_RES_START)) {
                        // 下位机通知app已经开始擦除
                        eraseFlash();
                        return;
                    } else if (ArrayUtils.startsWith(data, ERASE_RES_FINISH)) {
                        finishErase();
                        return;
                    }
                }
            });
            write(ERASE_CMD_START);
        });
    }

    private void initData() {
        mServiceConnection = new ServiceConnection() {
            @Override
            public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
                Log.d(TAG, "onServiceConnected");
                mBinder = (EcgFlashService.EcgFlashBinder) iBinder;
                if (mBinder != null) {
                    MyApplication.getHBDTestClient().setRunTimeFlag(SetArgumentsKeyEnum.ARG_LOST_SECTOR_ID_PACKAGE, "STOP");
                    mLostQuery = mBinder.isLostQuery();
                    runOnUiThread(() -> {
                        if (mLostQuery) {
                            statusTextView.setText(R.string.ecg_finish_reading_lost_data);
                            note.setText(R.string.ecg_finish_sync_lost_data);
                            eraseButton.setVisibility(View.INVISIBLE);
                        } else {
                            note.setText("");
                        }
                    });
                    setSectorId(true);
                }
            }

            @Override
            public void onServiceDisconnected(ComponentName componentName) {
                Log.d(TAG, "onServiceDisconnected");
                mBinder = null;
            }
        };

        Intent intent = new Intent(this, EcgFlashService.class);
        bindService(intent, mServiceConnection, Service.BIND_AUTO_CREATE);

        //状态恢复
        /*if (mBinder != null && mBinder.getMcuStatus()) {
            mBinder.recoveryStatus();
        }*/
    }

    private void stopGetSectorId(boolean destroy) {
        mAllSectorIdList.clear();
        mSectorIdList.clear();
        if (!mLostQuery || (mLostQuery && destroy)) {
            mBinder.finishLostSector();
        }
        MyApplication.getHBDTestClient().setRunTimeFlag(SetArgumentsKeyEnum.ARG_LOST_SECTOR_ID_PACKAGE, "STOP");
        Log.i(TAG,"stop get sector id");
    }

    private void setSectorId(boolean begin) {
        if (!mLostQuery && !begin) {
            mBinder.beginReadAllSector();
        }

        if ((mLostQuery && begin) || !begin) {
            Executors.newSingleThreadExecutor().execute(() -> {
                mAllSectorIdList.clear();
                mAllSectorIdList.addAll(mBinder.queryLostSectors(mSectorCallback));
                int size;
                if (mAllSectorIdList.size() > 50) {
                    size = 50;
                } else {
                    size = mAllSectorIdList.size();
                }
                for (int i = 0; i < size; i++) {
                    Integer integer = new Integer(mAllSectorIdList.get(i).intValue());
                    mSectorIdList.add(integer);
                }
                if (!mSectorIdList.isEmpty()) {
                    if (mSectorIdList.get(0) != ERROR_FAILED) {
                        if (mTotalNum == 0) {
                            mCalTime = System.currentTimeMillis();
                            mTotalNum = mAllSectorIdList.size();
                        }
                        runOnUiThread(() -> statusTextView.setTextColor(getResources().getColor(R.color.black)));
                        if (mLostQuery) {
                            runOnUiThread(() -> statusTextView.setText(R.string.ecg_finish_reading_lost_data));
                            MyApplication.getHBDTestClient().setRunTimeFlag(SetArgumentsKeyEnum.ARG_LOST_SECTOR_ID_PACKAGE,
                                    integerListToString(mSectorIdList));
                            Log.i(TAG,"setSectorId send sector id, " + integerListToString(mSectorIdList));
                        } else {
                            runOnUiThread(() -> statusTextView.setText(R.string.ecg_finish_reading_all_data));
                            MyApplication.getHBDTestClient().setRunTimeFlag(SetArgumentsKeyEnum.ARG_LOST_SECTOR_ID_PACKAGE, integerListToString(mSectorIdList));
                            Log.i(TAG,"setSectorId send all sector id, " + integerListToString(mSectorIdList));
                        }
                        timeOutThreadStart();
                    }
                    progressStart();
                } else {
                    runOnUiThread(() -> {
                        statusTextView.setText(R.string.ecg_finish_no_data_error);
                        resultImg.setImageDrawable(mContext.getDrawable(R.drawable.ic_check));
                        progressBar.setProgress(100);
                        note.setText(R.string.finish);
                        progressFinish();
                    });
                }
            });
        }
    }

    private void timeOutThreadStart() {
        if (mExecutorTimeOut == null) {
            mExecutorTimeOut = Executors.newSingleThreadScheduledExecutor();
            mExecutorTimeOut.scheduleAtFixedRate(() -> {
                mTimeOutNum.getAndAdd(1);
                if (mSectorIdList.size() > 0) {
                    if (mTimeOutNum.get() == BASIC_PROGRESS_TIME_OUT ||
                            mTimeOutNum.get() == 2 * BASIC_PROGRESS_TIME_OUT ||
                            mTimeOutNum.get() == 3 * BASIC_PROGRESS_TIME_OUT ||
                            mTimeOutNum.get() == 4 * BASIC_PROGRESS_TIME_OUT ||
                            mTimeOutNum.get() == 5 * BASIC_PROGRESS_TIME_OUT ||
                            mTimeOutNum.get() == 6 * BASIC_PROGRESS_TIME_OUT) {
                        MyApplication.getHBDTestClient().setRunTimeFlag(SetArgumentsKeyEnum.ARG_LOST_SECTOR_ID_PACKAGE, "STOP");
                        mSectorIdList.clear();
                        if (mTimeOutNum.get() == BASIC_PROGRESS_TIME_OUT ||
                                mTimeOutNum.get() == 2 * BASIC_PROGRESS_TIME_OUT ||
                                mTimeOutNum.get() == 3 * BASIC_PROGRESS_TIME_OUT ||
                                mTimeOutNum.get() == 4 * BASIC_PROGRESS_TIME_OUT ||
                                mTimeOutNum.get() == 5 * BASIC_PROGRESS_TIME_OUT) {
                            mLostQuery = true;
                            setSectorId(false);
                        } else if (mTimeOutNum.get() == 6 * BASIC_PROGRESS_TIME_OUT) {
                            runOnUiThread(() -> {
                                tryLostAlertDialog();
                            });
                        }
                    }
                }
            }, 0, 1000, TimeUnit.MILLISECONDS);
        }
    }

    private void tryLostAlertDialog() {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        if (mBinder.isLostQuery()) {
            builder.setTitle(R.string.ecg_finish_continue_get_lost_data);
        } else {
            builder.setTitle(R.string.ecg_finish_continue_get_all_data);
        }
        builder.setMessage(mContext.getString(R.string.ecg_finish_timeout_explain));
        builder.setCancelable(false);
        builder.setPositiveButton(R.string.btn_ok, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialogInterface, int i) {
                mTimeOutNum.set(0);
                runOnUiThread(() -> {
                    resultImg.setImageDrawable(null);
                });
                mLostQuery = true;
                setSectorId(false);
            }
        });
        builder.setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                runOnUiThread(() -> {
                    statusTextView.setTextColor(getResources().getColor(R.color.red));
                    statusTextView.setText(R.string.ecg_finish_get_data_time_out);
                    resultImg.setImageDrawable(mContext.getDrawable(R.drawable.ic_error));
                    if (mBinder.isLostQuery()) {
                        readButton.setVisibility(View.INVISIBLE);
                    }
                });
                Executors.newSingleThreadExecutor().execute(() -> {
                    progressFinish();
                    mBinder.forceFinish();
                    mBinder.resetStartMode();
                    mLostQuery = mBinder.isLostQuery();
                    stopGetSectorId(false);
                    mTimeOutNum.set(0);
                });
            }
        });
        builder.show();
    }

    private String integerListToString(List<Integer> list) {
        StringBuffer sb = new StringBuffer();
        if (list == null) {
            return null;
        }
        sb.append(StartArgumentsManager.getInstance().getTestId()).append(",");
        for (int i = 0; i < list.size(); i++) {
            if (i == list.size() - 1) {
                sb.append(list.get(i));
            } else {
                sb.append(list.get(i)).append(",");
            }
        }
        return sb.toString();
    }

    private void progressStart() {
        runOnUiThread(() -> {
            statusTextView.setTextColor(getResources().getColor(R.color.black));
            readButton.setText(getString(R.string.stop_reading_flash));
            eraseButton.setEnabled(false);
        });
        isReading = true;
    }

    private void progressFinish() {
        runOnUiThread(() -> {
            readButton.setText(getString(R.string.start_reading_flash));
            eraseButton.setEnabled(true);
        });
        isReading = false;
    }

    private void eraseFlash() {
        runOnUiThread(() -> {
            statusTextView.setTextColor(getResources().getColor(R.color.black));
            eraseButton.setEnabled(false);
            readButton.setEnabled(false);
            statusTextView.setText(R.string.erase_flash);
            progressBar.setProgress(0);
        });

        synchronized (isErasing) {
            isErasing = true;
        }
        mEraseTime = 0;
        new Thread(() -> {
            try {
                while (mEraseTime < ERASE_TIME_OUT) {
                    Thread.sleep(1000);
                    mEraseTime++;
                    runOnUiThread(() -> {
                        double progress = (double) mEraseTime * 100D / ERASE_TIME_OUT;
                        progressBar.setProgress((int) progress);
                        note.setText((String.format("%2.2f", progress) + "%"));
                    });
                    if (!isErasing) {
                        break;
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            if (isErasing) {
                Log.i(TAG,"Flash erase timeout!");
                runOnUiThread(() -> {
                    statusTextView.setText(R.string.ecg_finish_erase_time_out);
                    eraseButton.setEnabled(true);
                    readButton.setEnabled(true);
                    resultImg.setImageDrawable(mContext.getDrawable(R.drawable.ic_error));
                });
                synchronized (isErasing) {
                    isErasing = false;
                }
            } else {
                runOnUiThread(() -> {
                    eraseButton.setEnabled(true);
                    readButton.setEnabled(true);
                    progressBar.setProgress(100);
                    note.setText("100%");
                    statusTextView.setText(R.string.ecg_finish_erase_finish);
                    resultImg.setImageDrawable(mContext.getDrawable(R.drawable.ic_check));
                });
            }
        }).start();
    }

    private AbsOnHBDTestClientListener mClientListener = new AbsOnHBDTestClientListener() {

        @Override
        public void onDisconnected(int errCode) {
            Log.d(TAG, "onDisconnected errCode=" + errCode);
            runOnUiThread(() -> {
                statusTextView.setTextColor(getResources().getColor(R.color.red));
                statusTextView.setText(R.string.ecg_finish_ble_disconnected);
                resultImg.setImageDrawable(mContext.getDrawable(R.drawable.ic_error));
            });
        }

        @Override
        public void onConnected(int errCode) {
            Log.d(TAG, "onConnected errCode=" + errCode);
            if (errCode == IErrorCode.SUCCESS && isReading) {
                MyApplication.getHBDTestClient().setRunTimeFlag(SetArgumentsKeyEnum.ARG_LOST_SECTOR_ID_PACKAGE, "STOP");
                mSectorIdList.clear();
                mTimeOutNum.set(0);
                mLostQuery = true;
                setSectorId(false);
                runOnUiThread(() -> {
                    statusTextView.setTextColor(getResources().getColor(R.color.black));
                    if (mLostQuery) {
                        note.setText(R.string.ecg_finish_sync_lost_data);
                    } else {
                        note.setText(R.string.ecg_finish_sync_all_data);
                    }
                    resultImg.setImageDrawable(null);
                });
            }
        }
    };

    private void finishErase() {
        synchronized (isErasing) {
            isErasing = false;
        }

        write(ERASE_CMD_FINISH);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindService(mServiceConnection);
        mTotalNum = 0;
        mCalTime = 0;
        mCostTimeList.clear();
        mHbdTestClient.unRegisterTestCallback(mClientListener, null);
    }


    private void write(byte[] data) {
        if (bleConnection != null) {
            bleConnection.write(data);
        }
        Log.i(TAG, "Sent: " + ArrayUtils.toHexString(data));
    }

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
            if (isReading) {
                Toast.makeText(this, getString(R.string.wait_for_reading_finish), Toast.LENGTH_SHORT).show();
            } else {
                stopGetSectorId(true);
                if (bleConnection != null) {
                    bleConnection.setCallback(bleConnectionCallbackOriginal);
                }

                if (mExecutorTimeOut != null) {
                    mExecutorTimeOut.shutdown();
                    mExecutorTimeOut = null;
                }
                finish();
            }
            return true;
        } else {
            return super.dispatchKeyEvent(event);
        }
    }
}
